﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using SDT.ServiceSystem;
using System.Threading;
using SDT.Libraries.Utilities;
using SDT.DataService.Services;
using SDT.DataService;


namespace SDT.CrawlSystem.MediaSrv
{
    public sealed partial class MDWorkUnit : WorkUnit
    {
        #region Logging
        private static LogHelper Logger = new LogHelper(
          string.Format("{0}media.cache.log", Global.Instance.LogFile.Substring(0, Global.Instance.LogFile.LastIndexOf(Path.DirectorySeparatorChar) + 1)));

        internal static void FlushLog()
        {
            lock (Logger)
                Logger.Flush();
        }
        #endregion

        #region Data members
        private string url = "";
        public string Url
        {
            get { return url; }
        }
        public int CID { get; set; }
        public int PID { get; set; }
        public int LinkID { get; set; }
        public string PFile { get; set; }
        public string CreateDate { get; set; }


        #endregion

        #region Data Request
        public Uri ResourceUri;
        public IWebProxy HttpProxy;
        public string LocalSavePath = Global.Instance.DataFolder;
        public bool RecursiveGet;
        public ArrayList RetrievedUri,
                                PendingUri,
                                PendingBuffer;
        public ManualResetEvent BufferEvent;
        public int RequestTimeout;
        #endregion

        #region Constructor

        public MDWorkUnit(int Id, int cID, int pID, string _url, string date)
        {
            CID = cID;
            PID = pID;
            LinkID = Id;
            url = _url;
            CreateDate = date;
            PFile = GetFileServer(_url, cID, pID, date);
            ResourceUri = new Uri(_url);

            HttpProxy = WebRequest.DefaultWebProxy;
            LocalSavePath = ".";
            RecursiveGet = false;
            RetrievedUri = new ArrayList();
            PendingUri = new ArrayList();
            PendingBuffer = new ArrayList();
            BufferEvent = new ManualResetEvent(false);
            RequestTimeout = 30 * 1000;
        }
        #endregion

        #region Method

        public override void ClearResult()
        {
            lock (this)
            {
                Ready = true;
            }
        }

        private static void RequestTimeoutCallback(object state, bool timedOut)
        {
            if (timedOut)
            {
                HttpRequestState httpState = state as HttpRequestState;
                LinkDownloadService svLink = new LinkDownloadService();

                if ((httpState != null) && (httpState.httpRequest != null))
                {
                    httpState.httpRequest.Abort();
                }

                lock (httpState.WorkUnit)
                {
                    svLink.ChangeStatus(httpState.WorkUnit.LinkID, (byte)MDWorkUnit.Status.TIMEOUT);
                    httpState.WorkUnit.ClearResult();
                    lock (Logger)
                        Logger.AddEntry(String.Format("TIMEOUT @ UID = {0}", httpState.WorkUnit.LinkID));
                }


            }
        }

        private static void HttpStreamReadCallback(IAsyncResult ar)
        {
            HttpRequestState httpState = (HttpRequestState)ar.AsyncState;

            try
            {
                httpState.readBuffer.bytesRead = httpState.httpResponseStream.EndRead(ar);
                httpState.readBuffer.saveFile = httpState.saveFile;

                if (httpState.readBuffer.bytesRead > 0)
                {

                    httpState.bufferList.Add(httpState.readBuffer);

                    httpState.bufferEvent.Set();
                    httpState.readBuffer = new DataBuffer();
                    // Read the response into a Stream object.
                    PostResponseRead(httpState);
                }
                else
                {
                    httpState.httpResponseStream.Close();
                    httpState.httpResponse.Close();

                    httpState.readBuffer.done = true;
                    httpState.bufferList.Add(httpState.readBuffer);
                    httpState.bufferEvent.Set();
                }

            }
            catch (WebException wex)
            {
                (new MException()).WebException(httpState.WorkUnit.LinkID, httpState.WorkUnit.CID, httpState.WorkUnit.PID, wex);
            }
            catch (Exception ex)
            {
                Logger.AddEntry(string.Format("EXC: Module {0} - Message : {1} ", "HttpStreamReadCallback", ex.Message));
                return;
            }
        }
        private static void HttpResponseCallback(IAsyncResult ar)
        {
            HttpRequestState httpState = (HttpRequestState)ar.AsyncState;
            try
            {
                httpState.httpResponse = (HttpWebResponse)httpState.httpRequest.EndGetResponse(ar);
                // Read the response into a Stream object.
                httpState.httpResponseStream = httpState.httpResponse.GetResponseStream();
                httpState.saveFile = CreateFile(httpState.getUri, httpState.cID, httpState.pID, httpState.CreateDate, ref httpState.pFile);
                //Begin read response
                PostResponseRead(httpState);
                return;
            }
            catch (WebException wex)
            {
                (new MException()).WebException(httpState.WorkUnit.LinkID, httpState.WorkUnit.CID, httpState.WorkUnit.PID, wex);
                Logger.AddEntry(string.Format("EXC: Module {0} - Message : {1} ", "HttpResponseCallback", wex.Message));
            }
        }


        public static void PostResponseRead(HttpRequestState state)
        {
            Monitor.Enter(state);
            IAsyncResult asyncStreamRead = state.httpResponseStream.BeginRead(
                state.readBuffer.byteBuffer,
                0,
                DataBuffer.BufferSize,
                new AsyncCallback(HttpStreamReadCallback),
                state
                );

            Monitor.Exit(state);
        }

        public void GetResource(Uri getUri, bool recurse, MDWorkUnit workUnit)
        {
            lock (this)
            {
                HttpWebRequest httpRequest = null;
                HttpWebResponse httpResponse = null;
                FileStream localFile = null;
                LinkDownloadService svLink = new LinkDownloadService();

                try
                {
                    RetrievedUri.Add(getUri);
                    httpRequest = (HttpWebRequest)WebRequest.Create(getUri.AbsoluteUri);
                    httpRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 GTB6 (.NET CLR 3.5.30729)";
                    httpRequest.Proxy = HttpProxy;
                    //httpRequest.KeepAlive = false;
                    HttpRequestState getState = new HttpRequestState(httpRequest, this.PendingBuffer, this.BufferEvent, workUnit);

                    getState.localSavePath = this.LocalSavePath;
                    getState.getUri = getUri;
                    getState.cID = CID;
                    getState.pID = PID;
                    getState.CreateDate = CreateDate;

                    IAsyncResult ar = httpRequest.BeginGetResponse(new AsyncCallback(HttpResponseCallback), getState);

                    ThreadPool.RegisterWaitForSingleObject(
                        ar.AsyncWaitHandle, new WaitOrTimerCallback(RequestTimeoutCallback),
                        getState, RequestTimeout, true);
                }
                #region WebException
                catch (WebException wex)
                {
                    if (wex.Status == WebExceptionStatus.ProtocolError)
                    {
                        httpResponse = (HttpWebResponse)wex.Response;
                        httpResponse.Close();
                        httpResponse = null;
                    }
                    //Log Errot
                    (new MException()).WebException(workUnit.LinkID, workUnit.CID, workUnit.PID, wex);


                }
                #endregion
                finally
                {
                    // Close the resources if still open
                    if (localFile != null)
                        localFile.Close();

                    if (httpResponse != null)
                        httpResponse.Close();

                }
            }
        }

        public bool LoadUnitWork(int linkId, string date, string url, int cID, int pID)
        {
            MDWorkUnit httpGet = new MDWorkUnit(linkId, cID, pID, url, date);
            MDWorkUnit work = httpGet;
            LinkDownloadService svLink = new LinkDownloadService();

            bool getCompleted = false;
            string pFile = string.Empty;

            if (httpGet == null) return false;
            lock (httpGet)
            {

                try
                {
                    httpGet.GetResource(httpGet.ResourceUri, httpGet.RecursiveGet, work);

                    while (getCompleted == false)
                    {
                        if (httpGet.BufferEvent.WaitOne(5000, true) == true)
                        {
                            while (httpGet.PendingBuffer.Count > 0)
                            {
                                DataBuffer data = (DataBuffer)httpGet.PendingBuffer[0];
                                httpGet.PendingBuffer.RemoveAt(0);
                               
                                if (data.done == true)
                                {
                                    data.saveFile.Close();
                                    getCompleted = true;

                                    //ClearResult();
                                    if (!string.IsNullOrEmpty(httpGet.PFile))
                                    {

                                        #region Khóa file khi đang update file, tungnx
                                      
                                // Modifield by Vietlv
                                // Modifield date: 05/07/2011
                                // Description : Update path images to other database;
                                            //Do something
                                            if (svLink.UpdateLinkDownload(linkId, httpGet.PFile) > 0) //
                                        {
                                           //update DB
                                           // int uImg = new NewsService().ReplaceAllOldImage(pID);
                                            Logger.AddEntry(string.Format("Sycronize Add LinkID : {0}- C{1}- P{2} ", linkId, cID, pID));
                                            
                                        }

                                        #endregion
                                                                                
                                    }
                                    else
                                        Logger.AddEntry(string.Format("Add LinkID : {0}- C{1}- P{2} ", linkId, cID, pID));
                                    break;
                                }
                                else if (data.byteBuffer.Length > 0)
                                {

                                    data.saveFile.Write(data.byteBuffer, 0, data.bytesRead);
                                    
                                }
                            }
                            httpGet.BufferEvent.Reset();
                        }
                        else
                        {
                            break;
                        }

                    }
                }
                catch (NullReferenceException nre)
                {
                    Logger.AddEntry(string.Format("EXC:  {0} - LinkID : {1}- C{2}- P{3} ", nre.Message, linkId, cID, pID));
                    svLink.ChangeStatus(linkId, (byte)MDWorkUnit.Status.NA);
                    getCompleted = false;
                }

                catch (Exception ex)
                {
                    Logger.AddEntry(string.Format("EXC:  {0} - LinkID : {1}- C{2}- P{3} ", ex.Message, linkId, cID, pID));

                    svLink.ChangeStatus(linkId, (byte)MDWorkUnit.Status.NA);
                    getCompleted = false;
                }
            }
            return getCompleted;
        }

        #endregion


    }
}
